#include "computings.h"

#include <QDebug>

bool CTithi::IsValid()
{
    if ((num == 0) || (num > 15) || (false == dtBegin.isValid()) || (false == dtEnd.isValid()))
        return false;

    return true;
}
//---------------------------

QString CTithi::NameStr()
{
    QString tithi[16] = {"пратипад","двитйа","тритйа","чатуртхи","панчами","шаштхи","саптами","аштами","навами",
                       "дашами","экадаши","двадаши","трайодаши","чатурдаши","пурнима","амавасья"};

    if (false == IsValid())
        return "неизвестно";

    if ((false == paksha) && (15 == num))
        return tithi[15];
    return tithi[num-1];
}
//---------------------------

QString CTithi::AsTithiStr()
{
    QString str;

    str += "Начало: " + dtBegin.toString("d MMMM yyyy года  hh:mm") + "\n";
    str += "Конец:  " + dtEnd.toString("d MMMM yyyy года  hh:mm") + "\n";

    return str;
}
//---------------------------

QString CTithi::AsTithiStrExt()
{
    QString str;

    if (paksha)
        str += "шукла-пакша";
    else
        str += "кршна-пакша";
    str += " - ";
    str += NameStr();
    str += "\n";
    str += "(";
    if (paksha)
        str += "Растущая Луна";
    else
        str += "Убывающая Луна";
    str += " - ";
    str += QString::number(num) + " титхи";
    str += ")";
    str += "\n";

    str += "Начало: " + dtBegin.toString("d MMMM yyyy года  hh:mm") + "\n";
    str += "Конец:  " + dtEnd.toString("d MMMM yyyy года  hh:mm") + "\n";

    return str;
}
//---------------------------

QString CTithi::AsCurTithiStr()
{
    QString str;

    str = "Сейчас\n";
    str += AsTithiStrExt();

    return str;
}
//---------------------------

QString CTithi::AsEkadashStr()
{
    QString str;

    if (paksha)
        str += "шукла-пакша (Растущая Луна)\n";
    else
        str += "кршна-пакша (Убывающая Луна)\n";
    str += AsTithiStr();

    return str;
}
//---------------------------

void CTithi::RoundToMin()
{
    // округление времени до минуты
    QTime t;
    int second;

    t = dtBegin.time();
    second = t.second();
    t.setHMS(t.hour(),t.minute(),0);
    dtBegin.setTime(t);
    if (second >= SecsInMin/2)
        dtBegin.setMSecsSinceEpoch(dtBegin.toMSecsSinceEpoch() + MSecsInSec*SecsInMin);

    t = dtEnd.time();
    second = t.second();
    t.setHMS(t.hour(),t.minute(),0);
    dtEnd.setTime(t);
    if (second >= SecsInMin/2)
        dtEnd.setMSecsSinceEpoch(dtEnd.toMSecsSinceEpoch() + MSecsInSec*SecsInMin);
}
//---------------------------
//---------------------------------------------------------------------------------


double TimeEquation(int N)
{
  double B = 2*M_PI * (N - 81) / 365.F;
  return 9.87*sin(2*B) - 7.53*cos(B) - 1.5*sin(B);
}
//---------------------------

quint8 TithaNum(QDateTime dt)
{
    CAADate d;
    double JD, JDSunMoon, sunLng, moonLng;
    quint8 tithaNum = 0;

    if (dt.isValid())
    {
        d.Set(dt.date().year(),dt.date().month(),dt.date().day(),
              dt.time().hour(),dt.time().minute(),dt.time().second()+dt.time().msec()/double(MSecsInSec),true);
        JD = d.Julian();
        JDSunMoon = CAADynamicalTime::UTC2TT(JD);
        sunLng = CAASun::ApparentEclipticLongitude(JDSunMoon);
        moonLng = CAAMoon::EclipticLongitude(JDSunMoon);
        if (moonLng < sunLng)
            tithaNum = qCeil((moonLng + 360 - sunLng) / 12);
        else
            tithaNum = qCeil((moonLng - sunLng) / 12);
    }

    return tithaNum;
}
//---------------------------

CTithi SearchForCurrentTitha(double timeZoneOffset)
{
    CTithi titha;
    bool paksha = true, prevPaksha = paksha;
    QDateTime dt = QDateTime::currentDateTimeUtc(), dt1 = QDateTime::currentDateTimeUtc();
    qint64 step = -1 * MSecsInSec*SecsInMin*MinsInHour*(HoursInDay/2), prevStep = step, minStep = MSecsInSec*SecsInMin;
    bool f = true;
    quint8 tithaNum = 0, curTithaNum = 0;

    dt1 = dt;

    while(true)
    {
        tithaNum = TithaNum(dt);

        if (0 == tithaNum)
            break;
        else if (tithaNum > 15)
        {
            paksha = false;
            tithaNum-=15;
        }
        else
            paksha = true;

        if (f)
        {
            curTithaNum = tithaNum;
            titha.num = tithaNum;
            titha.paksha = paksha;
            f = false;
        }
        else if ((step < 0) && ((tithaNum < curTithaNum) || (paksha != prevPaksha)))
        {
            if (qAbs(step) > minStep)
            {
                dt.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() - step);
                step /= 2;
                dt.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() + step);
                continue;
            }

            titha.dtBegin.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() + qAbs(step) + (MSecsInSec*SecsInMin*MinsInHour*timeZoneOffset));

            dt = dt1;
            f = true;
            step = qAbs(prevStep);
            continue;
        }
        else if ((step > 0) && ((tithaNum > curTithaNum) || (paksha != prevPaksha)))
        {
            if (step > minStep)
            {
                dt.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() - step);
                step /= 2;
                dt.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() + step);
                continue;
            }

            titha.dtEnd.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() - step + (MSecsInSec*SecsInMin*MinsInHour*timeZoneOffset));
            titha.RoundToMin();

            break;
        }

        dt.setMSecsSinceEpoch(dt.toMSecsSinceEpoch() + step);
        prevPaksha = paksha;
    }

    return titha;
}
//---------------------------

QList<CTithi> SearchForTithi(QDateTime& date1, QDateTime& date2, double timeZoneOffset)
{
    QList<CTithi> tithi;
    CTithi titha;
    bool paksha = true, prevPaksha = paksha;
    qint32 step = MSecsInSec*SecsInMin*MinsInHour*HoursInDay, prevStep = step, minStep = MSecsInSec*SecsInMin;
    quint8 tithaNum = 0, prevTithaNum = 0;
    bool f = true, tf = false;

    while (date1 <= date2)
    {
        tithaNum = TithaNum(date1);
        if (0 == tithaNum)
            break;
        else if (tithaNum > 15)
        {
            tithaNum-=15;
            paksha = false;
        }
        else
            paksha = true;

        if(f)
        {
            prevTithaNum = tithaNum;
            prevPaksha = paksha;
            f = false;
        }
        else if ((tithaNum > prevTithaNum) || (paksha != prevPaksha))
        {
            if (step > minStep)
            {
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() - step);
                step /= 2;
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + step);
                continue;
            }

            if (tf)
            {
                titha.dtEnd.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() - step + (MSecsInSec * SecsInMin * MinsInHour * timeZoneOffset));

                titha.RoundToMin();
                tithi.append(titha);

                step = prevStep;
            }
            else
                tf = true;

            titha.dtBegin.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + (MSecsInSec * SecsInMin * MinsInHour * timeZoneOffset));
            titha.num = tithaNum;
            titha.paksha = paksha;

            prevTithaNum = tithaNum;
            prevPaksha = paksha;
        }
        date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + step);
    }

    return tithi;
}
//---------------------------

QList<CTithi> SearchForEkadashi(QDateTime& date1, QDateTime& date2, double timeZoneOffset)
{
    QList<CTithi> ekadashi;
    CTithi ekadash;
    quint8 tithaNum = 0;
    bool paksha = true;
    qint64 step = MSecsInSec*SecsInMin*MinsInHour*(HoursInDay/2), prevStep = step, minStep = MSecsInSec*SecsInMin;
    bool e = false, f = true;

    while (date1 <= date2)
    {
        tithaNum = TithaNum(date1);
        if (0 == tithaNum)
            break;
        else if (tithaNum > 15)
        {
            tithaNum-=15;
            paksha = false;
        }
        else
            paksha = true;

        if ((11 != tithaNum) && f)
            f = false;
        else if ((11 == tithaNum) && (false == e) && (false == f))
        {
            if (step > minStep)
            {
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() - step);
                step /= 2;
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + step);
                continue;
            }

            ekadash.dtBegin.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + (MSecsInSec * SecsInMin * MinsInHour * timeZoneOffset));
            ekadash.num = tithaNum;
            ekadash.paksha = paksha;

            step = prevStep;
            e = true;
        }
        else if ((12 == tithaNum) && e)
        {
            if (step > minStep)
            {
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() - step);
                step /= 2;
                date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + step);
                continue;
            }

            ekadash.dtEnd.setMSecsSinceEpoch((date1.toMSecsSinceEpoch() - step) + (MSecsInSec * SecsInMin * MinsInHour * timeZoneOffset));
            ekadash.RoundToMin();
            ekadashi.append(ekadash);

            e = false;
            step = prevStep;
        }

        date1.setMSecsSinceEpoch(date1.toMSecsSinceEpoch() + step);
    }

    return ekadashi;
}
//---------------------------

// Временные промежутки ведического летоисчисления в годах человеческого уровня
const quint64 Charana = 432000;
const quint64 SatyaYugaLen = 4*Charana;
const quint64 TretaYugaLen = 3*Charana;
const quint64 DvaparaYugaLen = 2*Charana;
const quint64 KaliYugaLen = Charana;
const quint64 MahaYugaLen = SatyaYugaLen + TretaYugaLen + DvaparaYugaLen + KaliYugaLen;
const quint64 ManvantaraNum = 7;
const quint64 MahaYugaInManvantaraNum = 28;
const quint64 ManvantaraLen = 71*MahaYugaLen;
const quint64 ManvantaraFullLen = 71*MahaYugaLen + 4*Charana;
const quint64 KalpaLen = 14*ManvantaraFullLen + 4*Charana;
const quint64 BrahmaDayLen = KalpaLen;
const quint64 UniverseDayLen = KalpaLen;
const quint64 BrahmaNightLen = BrahmaDayLen;
const quint64 UniverseNightLen = UniverseDayLen;
const quint64 BrahmaTwentyFourHours = BrahmaDayLen + BrahmaNightLen;
const quint64 UniverseTwentyFourHours = UniverseDayLen + UniverseNightLen;
const quint64 BrahmaYearLen = 360*BrahmaTwentyFourHours;
const quint64 UniverseYearLen = 360*UniverseTwentyFourHours;
const quint64 Parardha = 50*BrahmaYearLen;
const quint64 MahaKalpaLen = 100*BrahmaYearLen;
const quint64 BrahmaLifeLen = MahaKalpaLen;
const quint64 UniverseActiveLifeLen = MahaKalpaLen;
const quint64 UniverseNotActiveLifeLen = UniverseActiveLifeLen;
const quint64 UniverseLifeLen = UniverseActiveLifeLen + UniverseNotActiveLifeLen;

quint64 GregorianToVedicKaliYuga(qint32 year)
{
    if ((year < -3102) || (year == 0) || (year > 428898))
        return 0;

    QDate d = QDate::currentDate();
    if (d.month() > 2 || ((d.month() == 2) && (d.day() >= 18)))
    {
        if (year > 0)
            return year + 3102;
        else
            return 3102 + year + 1;
    }
    else
    {
        if (year > 0)
            return year + 3101;
        else
            return 3101 + year + 1;
    }
}
//---------------------------

quint64 GregorianToVedicMahaYuga(qint32 year)
{

    quint64 val = GregorianToVedicKaliYuga(year);
    if (0 != val)
        return SatyaYugaLen + TretaYugaLen + DvaparaYugaLen + val;
    else
        return val;
}
//---------------------------

quint64 GregorianToVedicManvantara(qint32 year)
{
    quint64 val = GregorianToVedicMahaYuga(year);
    if (0 != val)
        return (MahaYugaInManvantaraNum - 1)*MahaYugaLen + val;
    else
        return val;
}
//---------------------------

quint64 GregorianToVedicKalpa(qint32 year)
{
    quint64 val = GregorianToVedicManvantara(year);
    if (0 != val)
        return (ManvantaraNum - 1)*ManvantaraFullLen + val;
    else
        return val;
}
//---------------------------

quint64 GregorianToVedicMahaKalpa(qint32 year)
{
    quint64 val = GregorianToVedicKalpa(year);
    if (0 != val)
        return (Parardha + val);
    else
        return val;
}
//---------------------------
//---------------------------------------------------------------------------------
